package com.guojiang.guoaicodemother.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.guojiang.guoaicodemother.annotation.AuthCheck;
import com.guojiang.guoaicodemother.common.BaseResponse;
import com.guojiang.guoaicodemother.common.DeleteRequest;
import com.guojiang.guoaicodemother.common.ResultUtils;
import com.guojiang.guoaicodemother.constant.AppConstant;
import com.guojiang.guoaicodemother.constant.UserConstant;
import com.guojiang.guoaicodemother.exception.BusinessException;
import com.guojiang.guoaicodemother.exception.ErrorCode;
import com.guojiang.guoaicodemother.exception.ThrowUtils;
import com.guojiang.guoaicodemother.model.dto.appDto.*;
import com.guojiang.guoaicodemother.model.entity.App;
import com.guojiang.guoaicodemother.model.entity.User;
import com.guojiang.guoaicodemother.model.vo.appVo.AppVO;
import com.guojiang.guoaicodemother.service.AppService;
import com.guojiang.guoaicodemother.service.ProjectDownloadService;
import com.guojiang.guoaicodemother.service.UserService;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 应用 控制层。
 */
@RestController
@RequestMapping("/app")
public class AppController {

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;
    
    @Resource
    private ProjectDownloadService projectDownloadService;

    /**
     * 对话 AI 流式生成代码（最核心接口）
     *
     * @param appId       应用 ID
     * @param userMessage 用户输入信息
     * @param request     请求
     * @return 流式 SSE 返回
     */
    @GetMapping(value = "/chat/gen/code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> chatToGenCode(@RequestParam Long appId
            , @RequestParam String userMessage, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        // 调用服务生成代码（SSE 流式返回）
        Flux<String> codeStreaming = appService.chatToGenCode(appId, userMessage, loginUser);
        // 解决代码流式返回，前端解析时空格丢失问题，将代码流式返回封装成 ServerSentEvent<String>
        return codeStreaming.map(code -> {
            // 将生成的代码用 Map.of 创建静态不可变 Map 进行封装
            Map<String, String> wrapperMap = Map.of("d", code);
            // 转换成 JSON
            String jsonData = JSONUtil.toJsonStr(wrapperMap);
            // 封装为 ServerSentEvent
            return ServerSentEvent.<String>builder()
                    .data(jsonData)
                    .build();
        }).concatWith(Mono.just(
                // 发送结束标志
                ServerSentEvent.<String>builder()
                        .event("done")
                        .data("")
                        .build()
        ));
    }

    /**
     * 应用部署
     *
     * @param appDeployRequest 部署请求
     * @param request          请求
     * @return 部署 URL
     */
    @PostMapping("/deploy")
    public BaseResponse<String> deployApp(@RequestBody AppDeployRequest appDeployRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(ObjectUtil.isNull(appDeployRequest), ErrorCode.PARAMS_ERROR, "请求参数错误");
        User loginUser = userService.getLoginUser(request);
        Long appId = appDeployRequest.getAppId();
        // 调用服务部署应用
        String deployUrl = appService.deployApp(appId, loginUser);
        return ResultUtils.success(deployUrl);
    }

    /**
     * 打包压缩后下载应用代码
     *
     * @param appId    应用ID
     * @param request  请求
     * @param response 响应
     */
    @GetMapping("/download/{appId}")
    public void downloadAppCode(@PathVariable Long appId,
                                HttpServletRequest request,
                                HttpServletResponse response) {
        // 1. 基础校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID无效");
        // 2. 查询应用信息
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验：只有应用创建者可以下载代码
        User loginUser = userService.getLoginUser(request);
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限下载");
        }
        // 4. 构建应用代码目录路径（生成目录，非部署目录）
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 5. 检查代码目录是否存在
        File sourceDir = new File(sourceDirPath);
        ThrowUtils.throwIf(!sourceDir.exists() || !sourceDir.isDirectory(),
                ErrorCode.NOT_FOUND_ERROR, "应用代码不存在，请先生成代码");
        // 6. 生成下载文件名，格式示例："AppCode_123456_2025-08-11.zip"（六位数字是从倒数第二个数字开始往前六个数字）
        String appIdStr = String.valueOf(appId);
        String downloadFileName = "AppCode_" + appIdStr.substring(appIdStr.length() - 7, appIdStr.length()-1) 
                + DateUtil.format(LocalDateTime.now(),"_yyyy-MM-dd") + ".zip";
        // 7. 调用通用下载服务
        projectDownloadService.downloadProjectAsZip(sourceDirPath, downloadFileName, response);
    }

    // region 用户接口

    /**
     * 创建应用
     *
     * @param appAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addApp(@RequestBody AppAddRequest appAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        long result = appService.addApp(appAddRequest, request);
        return ResultUtils.success(result);
    }

    /**
     * 更新自己的应用
     *
     * @param appUpdateRequest
     * @param request
     * @return
     */
    @PostMapping("/update")
    public BaseResponse<Boolean> updateMyApp(@RequestBody AppUpdateRequest appUpdateRequest,
                                             HttpServletRequest request) {
        ThrowUtils.throwIf(appUpdateRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        boolean result = appService.updateMyApp(appUpdateRequest, request);
        return ResultUtils.success(result);
    }

    /**
     * 删除自己的应用
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteMyApp(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(deleteRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        boolean result = appService.deleteMyApp(deleteRequest, request);
        return ResultUtils.success(result);
    }

    /**
     * 根据 id 获取应用详情
     *
     * @param id 应用 id
     * @return 应用详情
     */
    @GetMapping("/get/my/vo")
    public BaseResponse<AppVO> getMyAppVOById(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR, "请求参数错误");
        // 获取封装类（包含用户信息）
        return ResultUtils.success(appService.getMyAppVOById(id, request));
    }

    /**
     * 根据 id 获取应用详情
     *
     * @param id 应用 id
     * @return 应用详情
     */
    @GetMapping("/get/vo")
    public BaseResponse<AppVO> getAppVOById(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        App app = appService.getById(id);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
        // 获取封装类（包含用户信息）
        return ResultUtils.success(appService.getAppVO(app));
    }

    /**
     * 分页获取自己的应用列表
     *
     * @param appQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/my/page/vo")
    public BaseResponse<Page<AppVO>> listMyAppVOByPage(@RequestBody AppQueryRequest appQueryRequest,
                                                       HttpServletRequest request) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.listMyAppVOByPage(appQueryRequest, request));
    }

    /**
     * 分页获取精选应用列表
     *
     * @param appQueryRequest
     * @return
     */
    @PostMapping("/list/good/page/vo")
    public BaseResponse<Page<AppVO>> listGoodAppVOByPage(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.listGoodAppVOByPage(appQueryRequest));
    }


    // endregion

    // region 管理员接口

    /**
     * 管理员删除任意应用
     *
     * @param deleteRequest
     * @return
     */
    @PostMapping("/admin/delete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> deleteAppForAdmin(@RequestBody DeleteRequest deleteRequest) {
        ThrowUtils.throwIf(deleteRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.deleteAppForAdmin(deleteRequest));
    }

    /**
     * 管理员更新任意应用
     *
     * @param appAdminUpdateRequest
     * @return
     */
    @PostMapping("/admin/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> adminUpdateApp(@RequestBody AppAdminUpdateRequest appAdminUpdateRequest) {
        ThrowUtils.throwIf(appAdminUpdateRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.adminUpdateApp(appAdminUpdateRequest));
    }

    /**
     * 管理员分页获取应用列表
     *
     * @param appQueryRequest
     * @return
     */
    @PostMapping("/admin/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<AppVO>> listAppVOByPageForAdmin(@RequestBody AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.listAppVOByPageForAdmin(appQueryRequest));
    }

    /**
     * 管理员根据 id 获取应用详情
     *
     * @param id
     * @return
     */
    @GetMapping("/admin/get/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<AppVO> getAppVOByIdForAdmin(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR, "请求参数错误");
        return ResultUtils.success(appService.getAppVOByIdForAdmin(id));
    }

    // endregion


}
